﻿using System;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;

namespace Fadd.Net
{
	public class SecureTcpClient
	{
		private Action _authenticationMethod;
		private X509Certificate2 _certificate;
		private IPEndPoint _endPoint;
		private SslProtocols _protocols;
		private SslStream _secureStream;
		private Socket _socket;
		private NetworkStream _stream;

		private SecureTcpClient()
		{
		}


		/// <summary>
		/// Gets or sets if socket should reconnect on errors.
		/// </summary>
		public bool IsReconnectEnabled { get; set; }

		public SslProtocols Protocols
		{
			get { return _protocols; }
		}

		public SslStream SecureStream
		{
			get { return _secureStream; }
		}

		public IPEndPoint EndPoint
		{
			get { return _endPoint; }
		}

		public string ServerName { get; set; }

		public static SecureTcpClient CreateClient(X509Certificate2 certificate, SslProtocols protocols, string serverName)
		{
			var client = new SecureTcpClient();
			client._certificate = certificate;
			client._protocols = protocols;
			client.ServerName = serverName;
			client._authenticationMethod = client.AuthenticateAsClient;
			return client;
		}

		public void Connect(IPEndPoint endPoint)
		{
			_endPoint = endPoint;
			_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			_socket.Connect(endPoint);
			_stream = new NetworkStream(_socket);
			_secureStream = new SslStream(_stream, false, OnValidateUserCertificate);
			_authenticationMethod();
		}

		protected virtual bool OnValidateUserCertificate(object sender, X509Certificate certificate, X509Chain chain,
		                                                 SslPolicyErrors sslpolicyerrors)
		{
			return true;
		}

		protected virtual void AuthenticateAsClient()
		{
			SecureStream.AuthenticateAsClient(ServerName, new X509CertificateCollection(new X509Certificate[] {_certificate}),
			                                  Protocols, true);
		}

		protected virtual void AuthenticateAsServer()
		{
			SecureStream.AuthenticateAsServer(_certificate, true, Protocols, true);
		}
	}

	public enum AuthenticationType
	{
		AuthenticateAsServer,
		AuthenticateAsClient
	}
}